﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Gw2Api;
using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;
using System.Collections;

namespace Gw2WvWMapControl
{
    public class WvWObjectiveDisplay : DependencyObject
    {
        public DateTime ts;
        public string owner;
        public string owner_world;
        public string name;
        public string owner_guild;
        public ObjectiveUpgradeDetails upgrade_details;
        public int points_tick;
        public WvWObjectiveDisplay(string name, string owner, string owner_world, string owner_guild, DateTime ts, ObjectiveUpgradeDetails upgrade_details, int points_tick)
        {
            this.owner = owner;
            this.owner_world = owner_world;
            this.owner_guild = owner_guild;
            this.name = name;
            this.ts = ts;
            this.upgrade_details = upgrade_details;
            this.points_tick = points_tick;
        }
    }

    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class WvWMapControl : UserControl
    {
        public class SliceUpdateEventArgs : EventArgs
        {
            public WvWObjectiveDiff diff;
            public SliceUpdateEventArgs(WvWObjectiveDiff d)
            {
                diff = d;
            }
        }
        public delegate void SliceUpdateHandler(object sender, SliceUpdateEventArgs data);
        public event SliceUpdateHandler SliceUpdate;

        private UIElement _currentHighlight;
        public static readonly DependencyProperty WvWApiProperty = DependencyProperty.Register(
                        "WvWApi", typeof(WvWApi), typeof(WvWMapControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
        public static readonly DependencyProperty WvWMapProperty = DependencyProperty.Register(
                        "WvWMap", typeof(WvWMaps), typeof(WvWMapControl), new FrameworkPropertyMetadata(WvWMaps.Center, FrameworkPropertyMetadataOptions.AffectsRender));
        public static readonly DependencyProperty WvWMatchupProperty = DependencyProperty.Register(
                        "WvWMatchup", typeof(string), typeof(WvWMapControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

        public static readonly DependencyProperty PositionApiProperty = DependencyProperty.Register(
                        "PositionApi", typeof(PositionApi), typeof(WvWMapControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
        
        public static readonly DependencyProperty HighlightObjectiveProperty = DependencyProperty.Register(
                        "HighlightObjective", typeof(WvWObjectiveDisplay), typeof(WvWMapControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
        public static readonly DependencyProperty ShowStatusTooltipProperty = DependencyProperty.Register(
                        "ShowStatusTooltip", typeof(bool), typeof(WvWMapControl), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None));

        private Panel _currentParent;
        private DispatcherTimer _timer;

        public WvWMapControl()
        {
            InitializeComponent();
            _currentParent = this.EB_Structures;
            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                _timer = new System.Windows.Threading.DispatcherTimer();
                _timer.Tick += new EventHandler(TimerTick);
                _timer.Interval = new TimeSpan(0, 0, 1);
                _timer.Start();
            }
        }

        private static void GetLogicalChildCollection<T>(DependencyObject parent, List<T> logicalCollection) where T : DependencyObject
        {
            IEnumerable children = LogicalTreeHelper.GetChildren(parent);
            foreach (object child in children)
            {
                if (child is DependencyObject)
                {
                    DependencyObject depChild = child as DependencyObject;
                    if (child is T)
                    {
                        logicalCollection.Add(child as T);
                    }
                    GetLogicalChildCollection(depChild, logicalCollection);
                }
            }
        }

        public UIElement HitTestTarget
        {
            get
            {
                if (null != _currentParent)
                {
                    return _currentParent;
                }
                else
                {
                    return this;
                }
            }
        }

        public WvWApi WvWApi
        {
            get { return (WvWApi)this.GetValue(WvWApiProperty); }
            set
            {
                WvWApi old = this.WvWApi;
                if (null != old)
                {
                    old.Update -= this.HandleUpdate;
                }
                this.SetValue(WvWApiProperty, value);
                RefreshMap();
                if (null != this.WvWMatchup)
                {
                    value.AddMatchupToRefreshList(this.WvWMatchup);
                }
                value.SetAutoUpdate(true);
                value.Update += this.HandleUpdate;
            } 
        }

        public WvWMaps WvWMap
        {
            get { return (WvWMaps)this.GetValue(WvWMapProperty); }
            set 
            {
                this.SetValue(WvWMapProperty, value);
                if (value == WvWMaps.Center)
                {
                    Canvas.SetZIndex(_currentParent, 0);
                    _currentParent.IsHitTestVisible = false;
                    _currentParent.Visibility = Visibility.Collapsed;
                    _currentParent = this.EB_Structures;
                    _currentParent.Visibility = Visibility.Visible;
                    _currentParent.IsHitTestVisible = true;
                    Canvas.SetZIndex(_currentParent, 1000);
                }
                else if (value == WvWMaps.BlueHome || value == WvWMaps.GreenHome || value == WvWMaps.RedHome)
                {
                    Canvas.SetZIndex(_currentParent, 0);
                    _currentParent.IsHitTestVisible = false;
                    _currentParent.Visibility = Visibility.Collapsed;
                    _currentParent = this.BL_Structures;
                    _currentParent.Visibility = Visibility.Visible;
                    _currentParent.IsHitTestVisible = true;
                    Canvas.SetZIndex(_currentParent, 1000);
                }
                RefreshMap();
            }
        }

        public string WvWMatchup
        {
            get { return (string)this.GetValue(WvWMatchupProperty); }
            set 
            { 
                this.SetValue(WvWMatchupProperty, value);
                if (null != value)
                {
                    this.WvWApi.AddMatchupToRefreshList(value);
                }
                RefreshMap(); 
            }
        }

        public PositionApi PositionApi
        {
            get { return (PositionApi)this.GetValue(PositionApiProperty); }
            set
            {
                this.SetValue(PositionApiProperty, value);
            }
        }

        public WvWObjectiveDisplay HighlightObjective
        {
            get { return (WvWObjectiveDisplay)this.GetValue(HighlightObjectiveProperty); }
            set 
            {
                this.SetValue(HighlightObjectiveProperty, value);
                if (null != value && ShowStatusTooltip)
                {
                    this.statusPopup.IsOpen = true;
                }
                else
                {
                    this.statusPopup.IsOpen = false;
                }
            }
        }

        public bool ShowStatusTooltip
        {
            get { return (bool)this.GetValue(ShowStatusTooltipProperty); }
            set { this.SetValue(ShowStatusTooltipProperty, value); }
        }

        private void RefreshMap()
        {
            List<Shape> logicalCollection = new List<Shape>();
            GetLogicalChildCollection(this, logicalCollection);
            foreach (var ele in logicalCollection)
            {
                Debug.WriteLine("Tag:" + ele.Tag + ", Width:" + ele.ActualWidth + ", Height:" + ele.ActualHeight);
            }
            if (this.WvWApi == null || this.WvWMatchup == null || this.WvWMatchup.Length == 0)
            {
                return;
            }
            this.WvWApi.AddMatchupToRefreshList(this.WvWMatchup);
            this.WvWApi.GetMatchup(this.WvWMatchup);
            this.WvWApi.SetAutoUpdate(true);
            List<WvWObjectiveDiff> changes = this.WvWApi.GetAllChangesInLast5Mins();
            foreach (UIElement uiele in _currentParent.Children)
            {
                if (!(uiele is Shape))
                    continue;
                Shape ele = uiele as Shape;
                Objective obj = this.WvWApi.GetObjectivePropertyByTagV2(this.WvWMatchup, this.WvWMap, ele.Tag.ToString());
                if (null != obj)
                {
                    string objectiveId = obj.id;
                    WvWFaction owner = this.WvWApi.GetObjectiveOwner(WvWMatchup, objectiveId);
                    string brushName;
                    switch (owner)
                    {
                        case WvWFaction.Red:
                            brushName = "Brush-R";
                            break;
                        case WvWFaction.Blue:
                            brushName = "Brush-B";
                            break;
                        case WvWFaction.Green:
                            brushName = "Brush-G";
                            break;
                        default:
                            brushName = "Brush-Default";
                            break;
                    }
                    if (changes.Select(d => d.objectiveId).Contains(objectiveId))
                    {
                        brushName += "-RI-1";
                    }
                    ele.Fill = (Brush)this.Resources[brushName];
                }
            }
        }

        private void HandleUpdate(List<BaseDiff> diffs)
        {
            this.Dispatcher.BeginInvoke(new BaseApi.UpdateHandler(DoUpdate), new object[] { diffs });
        }

        private void DoUpdate(List<BaseDiff> diffs)
        {
            if (this.PositionApi != null)
            {
                WvWMaps map = this.PositionApi.GetCurrentMap();
                if (map != this.WvWMap)
                {
                    this.WvWMap = map;
                }
            }
            foreach (WvWObjectiveDiff d in diffs)
            {
                if (d.matchupId != this.WvWMatchup)
                {
                    continue;
                }
                Objective obj = this.WvWApi.GetObjectivePropertyById(d.objectiveId);
                // If this objective is not on the map or is not known, skip it
                if (null == obj || (null != obj && obj.map_type != this.WvWMap))
                {
                    continue;
                }
                string brushName;
                if (d.to == "Red")
                {
                    brushName = "Brush-R-RI-3";
                }
                else if (d.to == "Blue")
                {
                    brushName = "Brush-B-RI-3";
                }
                else if (d.to == "Green")
                {
                    brushName = "Brush-G-RI-3";
                }
                else
                {
                    brushName = "Brush-Default";
                }

                string objName = obj.tag;
                foreach (Shape ele in _currentParent.Children)
                {
                    if (ele.Tag != null && ele.Tag.ToString() == objName)
                    {
                        ele.Fill = (Brush)this.Resources[brushName];
                        break;
                    }
                }
                OnSliceUpdate(new SliceUpdateEventArgs(d));
            }
        }

        protected void OnSliceUpdate(SliceUpdateEventArgs args)
        {
            if (SliceUpdate != null)
                SliceUpdate(this, args);
        }

        private void Path_MouseEnter(object sender, MouseEventArgs e)
        {
            Debug.WriteLine(e);
        }

        public void SetHighlightElement(Shape element)
        {
            if (null == this.WvWApi)
                return;
            if (element == _currentHighlight)
                return;

            ClearHighlightElement(element == null);
            _currentHighlight = element;
            TransformGroup tgroup = (TransformGroup)element.RenderTransform;
            ScaleTransform scale = new ScaleTransform(1.1, 1.1, 0.5, 0.5);
            tgroup.Children.Insert(0, scale);
            Objective obj = this.WvWApi.GetObjectivePropertyByTagV2(this.WvWMatchup, this.WvWMap, element.Tag.ToString());
            if (obj != null)
            {
                string id = obj.id;
                Gw2Api.WvWMatch.WvWMatchMap.WvWMatchObjective liveobj = WvWApi.GetLiveObjectiveById(this.WvWMatchup, id);
                IEnumerable<WvWObjectiveDiff> diffs = WvWApi.GetAllChangesInLast5Mins().Where(d => d.matchupId == this.WvWMatchup && d.objectiveId == id);
                ObjectiveUpgradeDetails upgradeDetails = WvWApi.GetUpgrades().First(u => u.id == obj.upgrade_id).GetObjectiveUpgradeDetails(liveobj.yaks_delivered);
                String worldName = Util.GetWorldName(WvWApi, this.WvWMatchup, liveobj.owner.ToString().ToLower());
                WvWObjectiveDisplay disp = new WvWObjectiveDisplay(WvWApi.GetObjectiveNameFromId(id), liveobj.owner.ToString(), worldName,
                    liveobj.GuildName, liveobj.last_flipped, upgradeDetails, liveobj.points_tick);
                /*if (diffs.Count() > 0)
                {
                    disp = new WvWObjectiveDisplay(WvWApi.GetObjectiveNameFromId(id), liveobj.owner.ToString(), liveobj.GuildName, diffs.First().ts);
                }
                else
                {
                    disp = new WvWObjectiveDisplay(WvWApi.GetObjectiveNameFromId(id), liveobj.owner.ToString(), liveobj.GuildName, DateTime.MinValue);
                }*/
                this.HighlightObjective = disp;
            }
        }

        public void ClearHighlightElement(bool hidePopup = true)
        {
            if (null == _currentHighlight)
            {
                return;
            }
            TransformGroup tgroup = (TransformGroup)_currentHighlight.RenderTransform;
            Transform tscale = null;
            foreach (var t in tgroup.Children)
            {
                if (t is ScaleTransform)
                {
                    tscale = t;
                    break;
                }
            }
            if (null != tscale)
            {
                tgroup.Children.Remove(tscale);
            }
            if (hidePopup)
            {
                _currentHighlight = null;
                this.HighlightObjective = null;
            }
        }

        private string GetBrushnameForObjectiveId(string objectiveId, List<WvWObjectiveDiff> changes1, List<WvWObjectiveDiff> changes2, List<WvWObjectiveDiff> changes3)
        {
            WvWFaction owner = this.WvWApi.GetObjectiveOwner(WvWMatchup, objectiveId);
            string brushName;
            switch (owner)
            {
                case WvWFaction.Red:
                    brushName = "Brush-R";
                    break;
                case WvWFaction.Blue:
                    brushName = "Brush-B";
                    break;
                case WvWFaction.Green:
                    brushName = "Brush-G";
                    break;
                default:
                    brushName = "Brush-Default";
                    break;
            }
            if (changes1.Where(d => d.matchupId == this.WvWMatchup).Select(d => d.objectiveId).Contains(objectiveId))
            {
                brushName += "-RI-1";
            }
            else if (changes2.Where(d => d.matchupId == this.WvWMatchup).Select(d => d.objectiveId).Contains(objectiveId))
            {
                brushName += "-RI-2";
            }
            else if (changes3.Where(d => d.matchupId == this.WvWMatchup).Select(d => d.objectiveId).Contains(objectiveId))
            {
                brushName += "-RI-3";
            }
            return brushName;
        }

        private void TimerTick(object sender, EventArgs evt)
        {
            if (null == this.WvWApi)
                return;
            List<WvWObjectiveDiff> changes3 = this.WvWApi.GetAllChangesBetweenTimespans(new TimeSpan(0, 0, 0), new TimeSpan(0, 3, 30));
            List<WvWObjectiveDiff> changes2 = this.WvWApi.GetAllChangesBetweenTimespans(new TimeSpan(0, 3, 30), new TimeSpan(0, 4, 30));
            List<WvWObjectiveDiff> changes1 = this.WvWApi.GetAllChangesBetweenTimespans(new TimeSpan(0, 4, 30), new TimeSpan(0, 5, 0));
            foreach (UIElement uiele in _currentParent.Children)
            {
                if (!(uiele is Shape))
                    continue;
                Shape ele = uiele as Shape;
                if (!(ele.Fill is LinearGradientBrush))
                    continue;
                Objective obj = this.WvWApi.GetObjectivePropertyByTagV2(this.WvWMatchup, this.WvWMap, ele.Tag.ToString());
                if (null != obj)
                {
                    string objectiveId = obj.id;
                    string brushName = GetBrushnameForObjectiveId(objectiveId, changes1, changes2, changes3);
                    ele.Fill = (Brush)this.Resources[brushName];
                }
            }
            this.statusText.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
        }
    }
}
